package com.atguigu.webflux.controller;

import com.atguigu.webflux.entity.FlightDO;
import com.atguigu.webflux.entity.PaymentDO;
import com.atguigu.webflux.entity.TicketDO;
import com.atguigu.webflux.entity.UserDO;
import com.atguigu.webflux.repositories.FlightRepostory;
import com.atguigu.webflux.repositories.PaymentRepostory;
import com.atguigu.webflux.repositories.TicketRepository;
import com.atguigu.webflux.repositories.WebToursUserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.http.server.reactive.ServerHttpResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import com.atguigu.webflux.schema.LoginForm;
import com.atguigu.webflux.schema.FlightStep1Form;
import com.atguigu.webflux.schema.FlightStep2Form;
import com.atguigu.webflux.schema.FlightStep3Form;
import com.atguigu.webflux.schema.SignupForm;

import java.net.URI;
import java.util.Optional;

import static com.atguigu.webflux.utils.RandomStringGenerator.generateAlphaString;
import static com.atguigu.webflux.utils.RandomStringGenerator.generateTimestampPart;

@Controller
public class WebTours {
  private static final Logger logger = LoggerFactory.getLogger(WebTours.class);

  private final WebToursUserRepository repository;
  private final FlightRepostory flightRepostory;
  private final PaymentRepostory paymentRepostory;
  private final TicketRepository ticketRepository;

  public WebTours(WebToursUserRepository repository, FlightRepostory flightRepostory, PaymentRepostory paymentRepostory, TicketRepository ticketRepository) {
    this.repository = repository;
    this.flightRepostory = flightRepostory;
    this.paymentRepostory = paymentRepostory;
    this.ticketRepository = ticketRepository;
  }

  @GetMapping("/error")
  public Mono<String> error(@RequestParam(name = "information", defaultValue = "") String information,
      @RequestParam(name = "level", required = false) Integer level,
      Model model) {
    model.addAttribute("message", information);
    model.addAttribute("level", level);
    return Mono.just("error");
  }

  @GetMapping("/signon")
  public Mono<String> signon(Model model) {
    String timestampPart = generateTimestampPart();
    String randomAlphaPart = generateAlphaString(28); // 根据需要调整长度
    String result = timestampPart + randomAlphaPart;
    model.addAttribute("userSession", result);
    return Mono.just("signon");
  }

  @GetMapping("/signoff")
  public Mono<String> signoff(Model model) {
    return Mono.just("signoff");
  }

  @GetMapping("/signup")
  public Mono<String> signup(Model model) {
    String timestampPart = generateTimestampPart();
    String randomAlphaPart = generateAlphaString(28); // 根据需要调整长度
    String result = timestampPart + randomAlphaPart;
    model.addAttribute("userSession", result);
    return Mono.just("signup");
  }

  @GetMapping("/flight")
  public Mono<String> flight(@RequestParam(name = "step", defaultValue = "1") int step, Model model) {
    logger.info("step:" + step);
    int nextStep = step + 1;
    if (nextStep >= 4) {
      nextStep = 1;
    }
    model.addAttribute("step", step);
    model.addAttribute("nextUrl", "/flight?step=" + nextStep);
    return Mono.just("flight");
  }

  @GetMapping("/itinerary")
  public Mono<String> itinerary(Model model) {
    Flux<PaymentDO> list = paymentRepostory.findAll();
    Mono<Long> length = list.count();
    model.addAttribute("list", list);
    model.addAttribute("length", length);
    return Mono.just("itinerary");
  }

  @GetMapping("/administration")
  public Mono<String> administration(Model model) {
    model.addAttribute("message", "Hello, WebFlux with Thymeleaf!");
    return Mono.just("administration");
  }

  @GetMapping("/home")
  public Mono<String> home(Model model, ServerWebExchange exchange) {
    return exchange.getSession()
        .map(session -> {
          String username = (String) session.getAttribute("username");
          model.addAttribute("username", username);
          return "home"; // 返回模板名称
        });
  }

  @PostMapping("/cgi-bin/step1/reservations.pl")
  public Mono<ResponseEntity<?>> step1(@Valid @ModelAttribute FlightStep1Form formData, ServerWebExchange exchange) {
    logger.info("Step 1 form data: {}", formData.toString());

    return flightRepostory.save(new FlightDO(formData.getDepart(), formData.getDepartDate(), formData.getArrive(), formData.getReturnDate(), formData.getNumPassengers(), formData.getSeatPref(), formData.getSeatType(), formData.getAdvanceDiscount(), formData.getUserId())).then(Mono.fromRunnable(() -> {
      ServerHttpResponse response = exchange.getResponse();
      response.setStatusCode(HttpStatus.FOUND);
      response.getHeaders().setLocation(URI.create("/flight?step=2"));
    }));
  }

  @PostMapping("/cgi-bin/step2/reservations.pl")
  public Mono<ResponseEntity<?>> step2(@Valid @ModelAttribute FlightStep2Form formData, ServerWebExchange exchange) {
    logger.info("Step 2 form data: {}", formData.toString());

    return ticketRepository.save(new TicketDO(formData.getOutboundFlight(), formData.getSeatPref(), formData.getSeatType(), formData.getAdvanceDiscount(), formData.getNumPassengers(), formData.getUserId())).then(Mono.fromRunnable(() -> {
      ServerHttpResponse response = exchange.getResponse();
      response.setStatusCode(HttpStatus.FOUND);
      response.getHeaders().setLocation(URI.create("/flight?step=3"));
    }));
  }

  @PostMapping("/cgi-bin/step3/reservations.pl")
  public Mono<ResponseEntity<?>> step3(@Valid @ModelAttribute FlightStep3Form formData, ServerWebExchange exchange) {
    logger.info("Step 3 form data: {}", formData.toString());

    // TODO: 这里的userId需要动态获取

    return paymentRepostory.save(new PaymentDO(1L, formData.getFirstName(), formData.getLastName(), formData.getAddress1(), formData.getAddress2(), formData.getPass1(), formData.getCreditCard(), formData.getExpDate(), formData.getOldCCOption(), formData.getSeatPref(), formData.getSeatType(), formData.getOutboundFlight(), formData.getReturnFlight(), formData.getAdvanceDiscount(), formData.getNumPassengers())).then(Mono.fromRunnable(() -> {
      ServerHttpResponse response = exchange.getResponse();
      response.setStatusCode(HttpStatus.FOUND);
      response.getHeaders().setLocation(URI.create("/itinerary"));
    }));
  }

  @PostMapping("/cgi-bin/signup")
  public Mono<ResponseEntity<?>> signupForm(@Valid @ModelAttribute SignupForm formData, ServerWebExchange exchange) {
    return repository.save(new UserDO(formData.getUsername(), formData.getPassword(), formData.getFirstName(), formData.getLastName(), formData.getAddress1(), formData.getAddress2(), "", "", true))
            .then(Mono.fromRunnable(() -> {
              ServerHttpResponse response = exchange.getResponse();
              response.setStatusCode(HttpStatus.FOUND);
              response.getHeaders().setLocation(URI.create("/signon"));
            }));
  }

  @PostMapping("/cgi-bin/login.pl")
  public Mono<ResponseEntity<?>> loginForm(@Valid @ModelAttribute LoginForm formData, ServerWebExchange exchange) {
    // TODO: 这里的用户名和密码需要进行真实的数据库查询校验，实际应用中应从认证逻辑中获取
    // 校验密码是否正确，如果错误，继续跳转到登录页面
    String username = formData.getUsername();
    logger.info("Login form: {}", formData.toString());
    // return Mono.just(Rendering.redirectTo("/home?username=" + username).build());
    // 获取session并将用户信息存入
    return exchange.getSession()
        .doOnNext(webSession -> webSession.getAttributes().put("username", username))
        .thenReturn(ResponseEntity.status(HttpStatus.FOUND)
            .header("Location", "/home")
            .build());
  }

  @PostMapping("/register")
  public Mono<Void> register(@Valid @ModelAttribute SignupForm formData, ServerWebExchange exchange) {
    return repository.save(new UserDO(formData.getUsername(), formData.getUsername(), formData.getFirstName(), formData.getLastName(), formData.getAddress1(), formData.getAddress2(), "", "", true))
            .then(Mono.fromRunnable(() -> {
              ServerHttpResponse response = exchange.getResponse();
              response.setStatusCode(HttpStatus.FOUND);
              response.getHeaders().setLocation(URI.create("/signon"));
            }));
  }

  @GetMapping("/api/webtours/users")
  @ResponseBody
  public Flux<UserDO> getAll() {
    return repository.findAll();
  }

  @GetMapping("/api/webtours/user/{id}")
  @ResponseBody
  public Mono<UserDO> getUser(@PathVariable Long id) {
    return repository.findById(id);
  }

  @PostMapping("/api/webtours/user")
  @ResponseBody
  public Mono<UserDO> create(@RequestBody UserDO user) {
    return repository.save(user);
  }

  @PutMapping("/api/webtours/user/{id}")
  @ResponseBody
  public Mono<UserDO> update(@PathVariable Long id, @RequestBody UserDO user) {
    return repository.findById(id).map(Optional::of).defaultIfEmpty(Optional.empty()).flatMap(item -> {
      if (item.isPresent()) {
        user.setId(id);
        return repository.save(user);
      }

      return Mono.empty();
    });
  }

  @DeleteMapping("/api/webtours/user/{id}")
  @ResponseBody
  public Mono<Void> deleteById(@PathVariable Long id) {
    return repository.deleteById(id);
  }

  @GetMapping("/api/webtours/payments")
  @ResponseBody
  public Flux<PaymentDO> getAllPayments() {
    return paymentRepostory.findAll();
  }

  @GetMapping("/api/webtours/payments/cancel")
  @ResponseBody
  public Flux<PaymentDO> cancelAllPayments() {
    // TODO: 取消所有订票
    return paymentRepostory.findAll();
  }
}
